MVC Pattern là gì?
MVC viết tắt của Model – View – Control là một Design Pattern rất phổ biến hiện nay. MVC là pattern dạng Architectural Design Pattern áp dụng khi xử lý các vấn đề liên quan đến kiến trúc ứng dụng.
Model là nơi lưu trữ dữ liệu người dùng, nó cho phép truy xuất dữ liệu để hiển thị hoặc thu thập dữ liệu. Model là cầu nối giữa thành phần View và Controller trong mẫu thiết kế này. Mục đích quan trọng nhất của nó là kết nối cơ sở dữ liệu, xử lý dữ liệu và chuẩn bị dữ liệu để chuyển đến các thành phần khác.
View là nơi dữ liệu được hiển thị, trong ứng dụng web View là một phần của hệ thống, nơi mà các mã HTML được sinh ra và hiển thị. View cũng là nơi nhận tương tác trực tiếp từ người dùng. Một vấn đề quan trọng là View không được lấy dữ liệu trực tiếp từ Controller mà phải thông qua Model.
Controller quản lý dữ liệu người dùng nhập vào và cập nhật sang Model, Controller chỉ được sử dụng khi có tương tác của người dùng, còn không nó không có giá trị. Controller chỉ đơn giản là thu thập thông tin và sau đó chuyển dữ liệu sang Model, nó không chứa bất kỳ logic nghiệp vụ nào. Controller kết nối với duy nhất một View và một Model tạo thành hệ thống dự liệu chạy theo một chiều (one way data flow system). ## Ví dụ Model – View – Controller pattern
Chúng ta cùng đến với một ví dụ cụ thể để thấy được cách áp dụng MVC trong thực tế, trong ví dụ này chúng ta sẽ xây dựng một ứng dụng quản lý game thủ đế chế (Nhân sự kiện giải AOE Việt Trung 2017). Tạo ra thư mục OOP/MVC để bắt đầu bạn nhé. Đầu tiên chúng ta tạo ra file Model.php với nội dung:
<?php
class Model {
private $gamers;
public function __construct() {
$this->gamers[] = array("name" => "Chim sẻ đi nắng", "adress" => "Đan Phượng, Hà Tây", "city" => "Hà Nội");
$this->gamers[] = array("name" => "Hồng Anh", "adress" => "Việt Trì, Phú Thọ", "city" => "Phú Thọ");
$this->gamers[] = array("name" => "Gunny", "adress" => "Nhổn, Hà Nội", "city" => "Hà Nội");
$this->gamers[] = array("name" => "Bibi", "adress" => "Hà Nội", "city" => "Hà Nội");
$this->gamers[] = array("name" => "Văn Sự", "adress" => "Hoằng Hóa, Thanh Hóa", "city" => "Thanh Hóa");
}
public function getAllGamers() {
return $this->gamers;
}
public function getGamerByName($gamerName) {
$key = array_search($gamerName, array_column($this->gamers, 'name'));
return $this->gamers[$key];
}
public function getGamersByCity($cityName) {
$keys = array_keys(array_column($this->gamers, 'city'), $cityName);
$cityarray = array();
foreach ($keys as $value) {
$cityarray[] = $this->gamers[$value];
}
return $cityarray;
}
public function addGamer($name, $adress, $city) {
$this->gamers[] = array("name" => $name, "adress" => $adress, "city" => $city);
}
}
Tiếp theo chúng ta tạo ra View.php với nội dung:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quản lý game thủ giải AOE Việt Trung 2017</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<h2><span class="glyphicon glyphicon-user"></span>Danh sách game thủ</h2>
<div class="row">
<div class="col-md-3">
<ul>
<?php
foreach ($gamers as $g) {
echo '<li>';
echo '<a href="', $_SERVER['SCRIPT_NAME'], '/PageController/getGamerByName/', $g['name'], '">', $g['name'], '</a><br>';
echo $g['adress'], '<br>';
echo '--<a href="', $_SERVER['SCRIPT_NAME'], '/PageController/getGamersByCity/', $g['city'], '">', $g['city'], '</a>';
echo '</li>';
}
?>
</ul>
</div><!--end col-->
<div class="col-md-5">
<ul>
<?php
if (isset($gamer)) {
echo 'Thông tin chi tiết về game thủ:';
echo '<li>';
echo '<a href="', $_SERVER['SCRIPT_NAME'], '/PageController/getGamerByName/', $gamer['name'], '">', $gamer['name'], '</a><br></li>';
echo '<li>', $gamer['adress'], '</li>';
echo '<li>', $gamer['city'], '</li>';
}
if (isset($gamersInCities)) {
echo 'Các game thủ ở: <b>', $gamersInCities[0]['city'], '</b>';
foreach ($gamersInCities as $g) {
echo '<li>';
echo '<a href="', $_SERVER['SCRIPT_NAME'], '/PageController/getGamersByCity/', $g['name'], '">', $g['name'], ' </a>';
echo $g['adress'], ' ';
echo $g['city'];
echo '</li>';
}
}
?>
</ul>
</div><!--end col-->
</div><!--end row-->
</div>
</body>
</html>
Cuối cùng chúng ta tạo ra Controller.php:
<?php
include_once './Model.php';
class Controller {
private $data;
private $model;
public function handle() {
$this->model = new Model();
// Luôn lấy dữ liệu tất cả các game thủ và đưa vào thuộc tính data
$this->assign("gamers", $this->model->getAllGamers());
// Tùy thuộc vào đường dẫn lấy các danh sách game thủ theo bộ lọc
if (isset($_SERVER['PATH_INFO'])) {
// Ví dụ: index.php/PageController/getPersonByName/Chim sẻ đi nắng
// Convert chuỗi ký tự thành mảng, giá trị thứ 2 của mảng chứa action, giá trị thứ 3 chứa thông tin của action
$pathinfo = explode("/", $_SERVER['PATH_INFO']);
switch ($pathinfo[2]) {
case "getGamerByName";
// $arr[3]="Chim sẻ đi nắng"
$this->getGamerByName(urldecode(trim($pathinfo[3])));
break;
case "getGamersByCity":
// $arr[3]="Hà Nội"
$this->getGamersByCity(urldecode(trim($pathinfo[3])));
break;
default:
$this->display("./View.php");
}
} else {
$this->display("./View.php");
}
}
public function getGamerByName($name) {
$this->assign("gamer", $this->model->getGamerByName($name));
$this->display("./View.php");
}
public function getGamersByCity($city) {
$this->assign("gamersInCities", $this->model->getGamersByCity($city));
$this->display("./View.php");
}
private function assign($key,$value){
$this->data[$key]=$value;
}
private function display($htmlPage){
extract($this->data);
include_once $htmlPage;
}
}
Như vậy, chúng ta đã có đủ 3 thành phần Model, View và Controller, tiếp theo chúng ta sử dụng cả 3 thành phần này để xử lý ứng dụng. Tạo file index.php là nơi tiếp nhận yêu cầu từ người dùng:
<?php
include('./Controller.php');
$controller = new Controller();
$controller->handle();
File này chỉ đơn giản là tạo ra một instance từ lớp Controller. Bạn có thể xem lại sơ đồ MVC ở đầu phần này, đây chính là nơi người dùng gửi yêu cầu HTTP đến hệ thống. Tiếp theo, Controller sẽ thực hiện gửi yêu cầu (gọi phương thức lấy dữ liệu từ Model) đến Model và Model trả về các dữ liệu cần thiết, các yêu cầu này phụ thuộc vào nhập liệu của người dùng là tham số thứ 2 trong đường dẫn, từ đó Controller có thể gọi đến các phương thức khác nhau của Model như getAllGamers, getGamerByName, getGamersByCity. Sau khi có dữ liệu trả về Controller gửi dữ liệu đến View thông qua việc gọi $this->display(“./View.php”); và View trả về định dạng HTML để hiển thị cho người dùng. Chúng ta cùng truy cập vào http://oop.dev/mvc để xem kết quả:
tên từng game thủ chúng ta có chi tiết thông tin game thủ đó, khi click vào từng thành phố sẽ ra danh sách các game thủ trong thành phố.
## Tại sao dùng MVC pattern?
Áp dụng MVC pattern có rât nhiều ưu điểm, dưới đây là các ưu điểm nổi bật:
- Giảm độ phức tạp của mã nguồn.
- Mã nguồn được sử dụng lại tốt hơn.
- Giảm bớt sự phụ thuộc trong code, dễ bảo trì, nâng cấp hơn.
- Có thể áp dụng nguyên lý IoC.